using CoreManager.Models.DTOs;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;

namespace CoreManager.Services.Auth
{
    /// <summary>
    /// JWT服务实现
    /// </summary>
    public class JwtService : IJwtService
    {
        private readonly IConfiguration _configuration;
        private readonly string _secretKey;
        private readonly string _issuer;
        private readonly string _audience;
        private readonly int _expiresInMinutes;

        public JwtService(IConfiguration configuration)
        {
            _configuration = configuration;
            _secretKey = configuration["Jwt:SecretKey"] ?? throw new ArgumentNullException("Jwt:SecretKey");
            _issuer = configuration["Jwt:Issuer"] ?? "CoreManager";
            _audience = configuration["Jwt:Audience"] ?? "CoreManager";
            _expiresInMinutes = int.Parse(configuration["Jwt:ExpiresInMinutes"] ?? "60");
        }

        public string GenerateAccessToken(UserInfoDto userInfo)
        {
            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_secretKey));
            var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

            var claims = new List<Claim>
            {
                new(ClaimTypes.NameIdentifier, userInfo.Id.ToString()),
                new(ClaimTypes.Name, userInfo.UserName),
                new(ClaimTypes.Email, userInfo.Email ?? ""),
                new("RealName", userInfo.RealName ?? ""),
                new("Avatar", userInfo.AvatarUrl ?? ""),
                new("Roles", JsonSerializer.Serialize(userInfo.Roles))
            };

            // 添加角色声明
            foreach (var role in userInfo.Roles)
            {
                claims.Add(new Claim(ClaimTypes.Role, role));
            }

            var token = new JwtSecurityToken(
                issuer: _issuer,
                audience: _audience,
                claims: claims,
                expires: DateTime.UtcNow.AddMinutes(_expiresInMinutes),
                signingCredentials: credentials
            );

            return new JwtSecurityTokenHandler().WriteToken(token);
        }

        public string GenerateRefreshToken()
        {
            var randomNumber = new byte[32];
            using var rng = RandomNumberGenerator.Create();
            rng.GetBytes(randomNumber);
            return Convert.ToBase64String(randomNumber);
        }

        public bool ValidateToken(string token)
        {
            try
            {
                var tokenHandler = new JwtSecurityTokenHandler();
                var key = Encoding.UTF8.GetBytes(_secretKey);
                
                tokenHandler.ValidateToken(token, new TokenValidationParameters
                {
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = new SymmetricSecurityKey(key),
                    ValidateIssuer = true,
                    ValidIssuer = _issuer,
                    ValidateAudience = true,
                    ValidAudience = _audience,
                    ValidateLifetime = true,
                    ClockSkew = TimeSpan.Zero
                }, out SecurityToken validatedToken);

                return true;
            }
            catch
            {
                return false;
            }
        }

        public UserInfoDto? GetUserInfoFromToken(string token)
        {
            try
            {
                var tokenHandler = new JwtSecurityTokenHandler();
                var jwtToken = tokenHandler.ReadJwtToken(token);

                var userInfo = new UserInfoDto
                {
                    Id = int.Parse(jwtToken.Claims.First(c => c.Type == ClaimTypes.NameIdentifier).Value),
                    UserName = jwtToken.Claims.First(c => c.Type == ClaimTypes.Name).Value,
                    Email = jwtToken.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Email)?.Value,
                    RealName = jwtToken.Claims.FirstOrDefault(c => c.Type == "RealName")?.Value,
                    AvatarUrl = jwtToken.Claims.FirstOrDefault(c => c.Type == "Avatar")?.Value
                };

                var rolesJson = jwtToken.Claims.FirstOrDefault(c => c.Type == "Roles")?.Value;
                if (!string.IsNullOrEmpty(rolesJson))
                {
                    userInfo.Roles = JsonSerializer.Deserialize<List<string>>(rolesJson) ?? new();
                }

                return userInfo;
            }
            catch
            {
                return null;
            }
        }
    }
}